package com.zc.angelica.config.sucurity.filter;


import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zc.angelica.config.sucurity.UserService;
import com.zc.angelica.config.sucurity.exceptions.LoginException;
import com.zc.angelica.config.sucurity.handler.LoginErrorHanler;
import com.zc.angelica.entity.Account;
import com.zc.angelica.entity.LastingLogin;
import com.zc.angelica.service.LastingLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

public class JwtAuthenticationTokenFilter extends BasicAuthenticationFilter {


    private LoginErrorHanler loginErrorHanler;

    private LastingLoginService lastingLoginService;

    private UserService userService;


    public JwtAuthenticationTokenFilter(AuthenticationManager authenticationManager, LoginErrorHanler loginErrorHanler, LastingLoginService lastingLoginService, UserService userService) {
        super(authenticationManager);
        this.lastingLoginService = lastingLoginService;
        this.loginErrorHanler = loginErrorHanler;
        this.userService = userService;

    }


    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        String authToken = httpServletRequest.getHeader("Authorization");

        if (authToken != null) {
            String clientCode = httpServletRequest.getHeader("clientCode");
            if (clientCode == null || clientCode.equals("")) {
                loginErrorHanler.onAuthenticationFailure(httpServletRequest, httpServletResponse, new LoginException("客户端code不能为空"));
                return;
            }
            JWT jwt = JWTUtil.parseToken(authToken);
            JWTPayload payload = jwt.getPayload();

            if (payload != null) {
                Boolean lasting = payload.getClaimsJson().getBool("lasting");
                Long accountId = payload.getClaimsJson().getLong("sub");
                String username = payload.getClaimsJson().getStr("name");
                if (lasting) {
                    /**
                     * 持久化验证
                     * */
                    if (!JWT.of(authToken).setKey("zhiruo".getBytes()).verify()) {
                        loginErrorHanler.onAuthenticationFailure(httpServletRequest, httpServletResponse, new LoginException("令牌已失效，请重新登录"));
                        return;
                    }
                    //获取平台code
                    QueryWrapper<LastingLogin> lastingLoginQueryWrapper = new QueryWrapper<>();
                    lastingLoginQueryWrapper.eq("account_id", accountId).eq("client_code", clientCode);
                    Long clientResult = lastingLoginService.getBaseMapper().selectCount(lastingLoginQueryWrapper);
                    if (clientResult == 0) {
                        loginErrorHanler.onAuthenticationFailure(httpServletRequest, httpServletResponse, new LoginException("令牌已失效，请重新登录"));
                        return;
                    }
                } else {
                    if (!JWT.of(authToken).setKey("zhiruo".getBytes()).validate(0)) { //如username不为空，并且能够在数据库中查到
                        loginErrorHanler.onAuthenticationFailure(httpServletRequest, httpServletResponse, new LoginException("令牌已失效，请重新登录"));
                        return;
                    }
                    /**
                     * token 续约
                     * */
                    Date expireDate = payload.getClaimsJson().getDate(JWTPayload.EXPIRES_AT);
                    // 判断剩余时间是否小于等于刷新时间
                    if (10 >= ((expireDate.getTime() - new Date().getTime()) / 1000 / 60)) {
                        Calendar calendar = new GregorianCalendar();
                        Date date = new Date();
                        calendar.setTime(date);
                        calendar.add(Calendar.MINUTE, 30);

                        Map<String, Object> jwtMap = new HashMap<String, Object>() {
                            private static final long serialVersionUID = 1L;

                            {
                                put("sub", accountId);
                                put("name", username);
                                put("exp", calendar.getTime());
                            }
                        };
                        String token = JWTUtil.createToken(jwtMap, "zhiruo".getBytes());
                        httpServletResponse.setHeader("Authorization", token);
                    }

                }
                try {
                    SecurityContextHolder.getContext().setAuthentication(getAuthentication(username));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(String username) throws Exception {

        Account userDetails = (Account) userService.loadUserByUsername(username);

        if (username != null) {
            return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        }
        return null;
    }
}

